package outPut

import (
	"encoding/json"
	"fmt"
	"github.com/xuri/excelize/v2"
	"db2s/global"
	"db2s/safety"
	"reflect"
	"sort"
	"strings"
	"sync"
)

//https://www.bookstack.cn/read/excelize-v2.0/spilt.6.4.md

type exl struct {
	xlsFileName        string
	xlsResultSheetName string
	xlsFin             *excelize.File
	event              string
	logThreadSeq       int64
	xlsSafetySwitch    bool
	xlsSheetNameGather []string
	xlsTailGather      map[string]string
}

func (e *exl) columnInit(columnLength int) (d []string) {
	var ff = map[int]string{1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H", 9: "I", 10: "J",
		11: "K", 12: "L", 13: "M", 14: "N", 15: "O", 16: "P", 17: "Q", 18: "R", 19: "Ss", 20: "T", 21: "U", 22: "V", 23: "W", 24: "X", 25: "Y", 26: "Z"}
	var columnRangeF = func(seq int) (s []string) {
		for k, v := range ff {
			if k <= seq {
				s = append(s, v)
			}
		}
		sort.Strings(s)
		return
	}
	if columnLength < 1 {
		columnLength = 1
	}
	if columnLength > 26 {
		columnLength = 26
	}
	switch columnLength % 26 {
	case 0:
		return columnRangeF(26)
	default:
		return columnRangeF(columnLength % 26)
	}
}

func (e *exl) CreateFile() (b bool, err error) {
	var (
		index    int
		password string
	)
	e.event = "[xls result output]"
	if e.xlsSafetySwitch {
		password = safety.GenPasswd()
		//WLog.Warn(fmt.Sprintf("(%d) %s The security password of the currently produced %s sheet %s is: %s . for safety reasons, please keep the password safe", e.logThreadSeq, e.event, e.xlsFileName, e.xlsResultSheetName, password))
	}
	e.xlsFin = excelize.NewFile()
	defer func() {
		if err = e.xlsFin.Close(); err != nil {
			return
		}
	}()
	for k, v := range e.xlsSheetNameGather {
		if index, err = e.xlsFin.NewSheet(v); err != nil {
			return
		} else {
			if v1, ok := e.xlsTailGather[v]; ok {
				e.AppendWrite(v, v1)
			}
		}
		if e.xlsSafetySwitch {
			if err = e.xlsFin.ProtectSheet(v, &excelize.SheetProtectionOptions{
				Password:      password,
				EditScenarios: false,
				EditObjects:   false,
			}); err != nil {
				return
			}
		}
		if k == 0 {
			e.xlsFin.SetActiveSheet(index)
		}
	}
	if err = e.xlsFin.DeleteSheet("Sheet1"); err != nil {
		return
	}
	return true, nil
}
func (e *exl) Read() any {
	return nil
}
func (e *exl) Read1() any {
	return nil
}
func (e *exl) setColor() int {
	var style1 excelize.Style
	if err := json.Unmarshal([]byte(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":1}}`), &style1); err != nil {
		return 0
	}
	style, err := e.xlsFin.NewStyle(&style1)
	if err != nil {
		fmt.Println("setColor error:", err)
	}
	return style
}
func (e *exl) chanStringAction(writes <-chan string, sheetName string) bool {
	global.ScLock.Lock()
	for k1 := range writes {
		sf := strings.Split(k1, global.StructColumnSplit)
		i := e.GetLastUseRows(sheetName)
		if i == 1 {
			e.firstRowsSetStyle(sheetName, sf)
		}
		if err := e.xlsFin.SetSheetRow(sheetName, fmt.Sprintf("A%d", i), &sf); err != nil {
			fmt.Println("---error:", err)
		}
	}
	global.ScLock.Unlock()
	return true
}
func (e *exl) sliceStringAction(writes []string, sheetName string) bool {
	global.ScLock.Lock()
	for _, k1 := range writes {
		sf := strings.Split(k1, global.StructColumnSplit)
		i := e.GetLastUseRows(sheetName)
		if i == 1 {
			e.firstRowsSetStyle(sheetName, sf)
		}
		if err := e.xlsFin.SetSheetRow(sheetName, fmt.Sprintf("A%d", i), &sf); err != nil {
			fmt.Println("---error:", err)
		}
	}
	global.ScLock.Unlock()
	return true
}
func (e *exl) mapStringAction(writes map[string]string) bool {
	global.ScLock.Lock()
	for k1, v1 := range writes {
		sf := strings.Split(v1, global.StructColumnSplit)
		i := e.GetLastUseRows(k1)
		if i == 1 {
			e.firstRowsSetStyle(k1, sf)
		}
		if err := e.xlsFin.SetSheetRow(k1, fmt.Sprintf("A%d", i), &sf); err != nil {
			fmt.Println("---error:", err)
		}
	}
	global.ScLock.Unlock()
	return true
}
func (e *exl) firstRowsSetStyle(sheetName string, sf []string) {
	s := e.columnInit(len(sf))
	for k := range sf {
		rowSeq := fmt.Sprintf("%s%d", s[k], 1)
		err := e.xlsFin.SetCellStyle(sheetName, rowSeq, rowSeq, e.setColor())
		if err != nil {
			fmt.Println("first rows setStyle error", err)
		}
	}
}
func (e *exl) StringAction(writes string, sheetName string) bool {
	global.ScLock.Lock()
	sf := strings.Split(writes, global.StructColumnSplit)
	i := e.GetLastUseRows(sheetName)
	if i == 1 {
		e.firstRowsSetStyle(sheetName, sf)
	}
	if err := e.xlsFin.SetSheetRow(sheetName, fmt.Sprintf("A%d", i), &sf); err != nil {
		fmt.Println("---error:", err)
	}
	global.ScLock.Unlock()
	return true
}
func (e *exl) AppendWrite(sheetName string, writeS any) (b bool) {
	switch reflect.TypeOf(writeS).String() {
	case "string":
		return e.StringAction(writeS.(string), sheetName)
	case "[]string":
		return e.sliceStringAction(writeS.([]string), sheetName)
	case "chan string":
		return e.chanStringAction(writeS.(chan string), sheetName)
	case "map[string]string":
		return e.mapStringAction(writeS.(map[string]string))
	}
	return true
}
func (e *exl) GetLastUseRows(sheetName string) (d int64) {
	var i int64 = 1
	for {
		if s, err := e.xlsFin.GetCellValue(sheetName, fmt.Sprintf("A%d", i)); err != nil {
			fmt.Println("----1:", sheetName, err)
			return
		} else {
			if s == "" {
				return i
			}
		}
		i++
	}
}
func (e *exl) Close() (err error) {
	// 根据指定路径保存文件
	if err = e.xlsFin.SaveAs(e.xlsFileName); err != nil {
		return err
	}
	if err = e.xlsFin.Close(); err != nil {
		return err
	}
	return
}

type (
	TaskResultSheet struct {
		TextCN          string `json:"文本列名"`
		TextCT          string `json:"文本类型"`
		IndexName       string `json:"索引名"`
		IndexType       string `json:"索引类型"`
		IndexCol        string `json:"索引列"`
		DataLength      string `json:"数据大小(MB)"`
		CardinalityRate string `json:"离散度"`
		RateEvaluation  string `json:"优先级"`
		Sum             int64  `json:"总行数"`
		PartitionsSum   int64  `json:"分区数量"`
		PartitionsType  string `json:"分区类型"`
		TaskName        string `json:"任务名称"`
	}
	StructResultSheet struct {
		Behavior string `json:"行为"`   /* alter | create */
		FixFay   string `json:"修复方式"` /*修复方式，目前有file，table*/
		Exec     string `json:"执行状态"` /*执行状态，成功、失败*/
		ErrInfo  string `json:"错误信息"`
		FixSql   string `json:"修复语句"`
		RawSql   string `json:"原始sql"`
	}
	SyncResultSheet struct {
		SyncSum            *int64 `json:"任务启动时源端获取记录数"` //表的实际行数
		SelectRows         *int64 `json:"迁移过程中获取行数"`    //源端查询装填
		InsertRows         *int64 `json:"迁移过程中写入行数"`    //目标端写入状态
		SyncStatus         string `json:"任务状态"`         //同步状态   0为正常，-1为异常
		TableCost          string `json:"单表迁移耗时(s)"`
		MigrationRate      string `json:"迁移速率 rows/s"`
		SourceDataSize     string `json:"源端数据量(MB)"`
		DestDataSize       string `json:"目标端数据量(MB)"`
		DataExpansionRatio string `json:"数据膨胀比(源端MB/目标端MB)"`
	}
	SheetResultData struct {
		Seq     int    `json:"任务序号"`
		Schema  string `json:"库名"`
		Table   string `json:"对象名"`
		Role    string `json:"角色"`
		Mode    string
		Task    TaskResultSheet   `json:"task"`
		Struct  StructResultSheet `json:"struct"`
		Object  StructResultSheet `json:"object"`
		Sync    SyncResultSheet   `json:"sync"`
		Comment string            `json:"备注"` //主要描述错误信息的记录
	}
)

type StructSwitchSheet struct {
	Behavior          string `json:"行为"`    /* alter | create */
	IssueType         string `json:"类型"`    /* 用于区分表中不同属性，table、index可选值：index primary、index unique 、index name   column可选值：column name、column type、column null、column default column comment等*/
	Active            string `json:"动作"`    /*用于需要执行的动作，可选值：drop、add、modify、copy*/
	Logo              string `json:"标识"`    /*(列名|索引名)*/
	SrcValue          string `json:"源端列属性"` /* 记录源端的值，可以是多个*/
	DstValue          string `json:"目端列属性"`
	FixRecommendation string `json:"修复意见"` /*记录因为异构数据库导致的数据差异*/
}
type SheetSwitchData struct {
	Seq     int    `json:"任务序号"`
	Schema  string `json:"库名"`
	Table   string `json:"对象名"`
	Mode    string
	Role    string            `json:"角色"`
	Struct  StructSwitchSheet `json:"struct"`
	Comment string            `json:"备注"` //主要描述错误信息的记录
}
type SheetMiss struct {
	Schema  string `json:"库名"`
	Table   string `json:"对象名"`
	Role    string `json:"角色"`
	Missing string `json:"缺失源"`
}
type WorkSheetResult struct {
	SheetName   string `json:"result"`
	Persistence string //持久化路径
	Data        SheetResultData
}
type WorkSheetTerminal struct {
	SheetName   string `json:"terminal"`
	Persistence string //持久化路径
	Data        TPod
}
type WorkSheetMiss struct {
	SheetName   string `json:"miss"`
	Persistence string //持久化路径
	Data        SheetMiss
}
type WorkSheetSwitch struct {
	SheetName   string `json:"switch"`
	Persistence string //持久化路径
	Data        SheetSwitchData
}
type Cell struct {
	Wg            *sync.WaitGroup
	C             chan any
	res           []any
	ModifyValFunc global.EFunc
	ExecFunc      []global.EFunc
}

func (e *Cell) Init() {
	//e.Wg = new(sync.WaitGroup)
}
func (e *Cell) Write(s any) {
	e.C <- s
}
func (e *Cell) Start() {
	e.Init()
}
func (e *Cell) Load(pods string, value any) (err error) {
	return dynamicModifyValue(e, pods, value)
}
func (e *Cell) AddFunc(f global.EFunc) {
	e.ExecFunc = append(e.ExecFunc, f)
}
func (e *Cell) Run() {
	e.Wg.Add(1)
	go func() {
		defer func() {
			e.Wg.Done()
		}()
		for {
			select {
			case c, d := <-e.C:
				if !d {
					return
				}
				if c == nil {
					continue
				}
				var x = c
				if e.ModifyValFunc.FuncName != nil {
					x = e.ModifyValFunc.FuncName("", c)
				}
				for _, v := range e.ExecFunc {
					if v.FuncName != nil {
						v.FuncName("", x)
					}
				}
			}
		}
	}()
}
func (e *Cell) Wait() {
	e.Wg.Wait()
}
func (e *Cell) Stop() {
	close(e.C)
}
func (e *Cell) SetExecFunc(f global.EFunc) {
	e.ExecFunc = []global.EFunc{f}
}
func (e *Cell) SetWaitGroup(wg *sync.WaitGroup) {
	e.Wg = wg
}
func (e *Cell) setColor() error {
	return nil
}
func (e *Cell) Print() {
	//switch s {
	//case "name":
	//
	//}
}

type Xls struct {
	FileName  string
	fin       *excelize.File
	SheetName []string
	Safety    bool
	Mode      string
	Enroll    []any
	password  string
}

func (e *Xls) Init() (err error) {
	var fin any
	if fin, err = e.Open(e.FileName); err != nil {
		return
	}
	switch fin.(type) {
	case *excelize.File:
		e.fin = fin.(*excelize.File)
	}
	return
}
func (e *Xls) GetPassword() string {
	return e.password
}
func (e *Xls) Head() (err error) {
	for _, v := range e.Enroll {
		var writeStr []string
		var sheetName string
		val := reflect.ValueOf(v)
		// 如果 s 是指针，获取指向的值
		if val.Kind() == reflect.Ptr {
			val = val.Elem()
		}
		tType := val.Type()
		for i := 0; i < tType.NumField(); i++ {
			switch tType.Field(i).Type.Kind() {
			case reflect.Struct:
				for _, vv := range RecursiveStructJsonName(e.Mode, val.Field(i).Interface()) {
					writeStr = append(writeStr, fmt.Sprintf("%v", vv))
				}
			case reflect.Ptr:
				for _, vv := range RecursiveStructJsonName(e.Mode, val.Field(i).Interface()) {
					writeStr = append(writeStr, fmt.Sprintf("%v", vv))
				}
			case reflect.String:
				switch tType.Field(i).Name {
				case "SheetName":
					sheetName = val.Type().Field(i).Tag.Get("json")
				}
			default:

			}
		}
		if err = e.Write(sheetName, strings.Join(writeStr, CSymbol)); err != nil {
			return
		}
	}
	return
}
func (e *Xls) Open(_ string) (res any, err error) {
	return
}
func (e *Xls) Close() (err error) {
	// 根据指定路径保存文件
	if err = e.fin.SaveAs(e.FileName); err != nil {
		return err
	}
	if err = e.fin.Close(); err != nil {
		return err
	}
	return
}
func (e *Xls) Read() (res any, err error) {
	return
}
func (e *Xls) Create() (err error) {
	var index int
	if e.Safety {
		e.password = safety.GenPasswd()
	}
	e.fin = excelize.NewFile()
	defer func() {
		if err = e.fin.Close(); err != nil {
			return
		}
	}()
	for k, v := range e.SheetName {
		if index, err = e.fin.NewSheet(v); err != nil {
			return
		} else {
			//if v1, ok := e.xlsTailGather[v]; ok {
			//	e.AppendWrite(v, v1)
			//}
		}
		if e.Safety {
			if err = e.fin.ProtectSheet(v, &excelize.SheetProtectionOptions{
				Password:      e.password,
				EditScenarios: false,
				EditObjects:   false,
			}); err != nil {
				return
			}
		}
		if k == 0 {
			e.fin.SetActiveSheet(index)
		}
	}
	if err = e.fin.DeleteSheet("Sheet1"); err != nil {
		return
	}
	return
}
func (e *Xls) Print(s Print) (r any) {
	switch s {
	case FName:
		return e.FileName
	}
	return
}
func (e *Xls) columnInit(columnLength int) (d []string) {
	var ff = map[int]string{1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H", 9: "I", 10: "J",
		11: "K", 12: "L", 13: "M", 14: "N", 15: "O", 16: "P", 17: "Q", 18: "R", 19: "Ss", 20: "T", 21: "U", 22: "V", 23: "W", 24: "X", 25: "Y", 26: "Z"}
	var columnRangeF = func(seq int) (s []string) {
		for k, v := range ff {
			if k <= seq {
				s = append(s, v)
			}
		}
		sort.Strings(s)
		return
	}
	if columnLength < 1 {
		columnLength = 1
	}
	if columnLength > 26 {
		columnLength = 26
	}
	switch columnLength % 26 {
	case 0:
		return columnRangeF(26)
	default:
		return columnRangeF(columnLength % 26)
	}
}
func (e *Xls) GetLastUseRows(sheetName string) (d int64) {
	var i int64 = 1
	for {
		if s, err := e.fin.GetCellValue(sheetName, fmt.Sprintf("A%d", i)); err != nil {
			return
		} else {
			if s == "" {
				return i
			}
		}
		i++
	}
}
func (e *Xls) setColor() int {
	var style1 excelize.Style
	if err := json.Unmarshal([]byte(`{"fill":{"type":"pattern","color":["#E0EBF5"],"pattern":1}}`), &style1); err != nil {
		return 0
	}
	style, err := e.fin.NewStyle(&style1)
	if err != nil {
		fmt.Println("setColor error:", err)
	}
	return style
}
func (e *Xls) chanStringAction(writes <-chan string, sheetName string) (err error) {
	global.ScLock.Lock()
	for k1 := range writes {
		sf := strings.Split(k1, CSymbol)
		i := e.GetLastUseRows(sheetName)
		if i == 1 {
			e.firstRowsSetStyle(sheetName, sf)
		}
		if err = e.fin.SetSheetRow(sheetName, fmt.Sprintf("A%d", i), &sf); err != nil {
			return
		}
	}
	global.ScLock.Unlock()
	return
}
func (e *Xls) sliceStringAction(writes []string, sheetName string) (err error) {
	global.ScLock.Lock()
	for _, k1 := range writes {
		sf := strings.Split(k1, CSymbol)
		i := e.GetLastUseRows(sheetName)
		if i == 1 {
			e.firstRowsSetStyle(sheetName, sf)
		}
		if err = e.fin.SetSheetRow(sheetName, fmt.Sprintf("A%d", i), &sf); err != nil {
			return
		}
	}
	global.ScLock.Unlock()
	return
}
func (e *Xls) mapStringAction(writes map[string]string) (err error) {
	global.ScLock.Lock()
	for k1, v1 := range writes {
		sf := strings.Split(v1, CSymbol)
		i := e.GetLastUseRows(k1)
		if i == 1 {
			e.firstRowsSetStyle(k1, sf)
		}
		if err = e.fin.SetSheetRow(k1, fmt.Sprintf("A%d", i), &sf); err != nil {
			return
		}
	}
	global.ScLock.Unlock()
	return
}
func (e *Xls) firstRowsSetStyle(sheetName string, sf []string) {
	s := e.columnInit(len(sf))
	for k := range sf {
		rowSeq := fmt.Sprintf("%s%d", s[k], 1)
		err := e.fin.SetCellStyle(sheetName, rowSeq, rowSeq, e.setColor())
		if err != nil {
			fmt.Println("first rows setStyle error", err)
		}
	}
}
func (e *Xls) StringAction(writes string, sheetName string) (err error) {
	global.ScLock.Lock()
	sf := strings.Split(writes, CSymbol)
	i := e.GetLastUseRows(sheetName)
	if i == 1 {
		e.firstRowsSetStyle(sheetName, sf)
	}
	if err = e.fin.SetSheetRow(sheetName, fmt.Sprintf("A%d", i), &sf); err != nil {
		return
	}
	global.ScLock.Unlock()
	return
}
func (e *Xls) Write(sheetName string, writeS any) (err error) {
	switch reflect.TypeOf(writeS).String() {
	case "string":
		return e.StringAction(writeS.(string), sheetName)
	case "[]string":
		return e.sliceStringAction(writeS.([]string), sheetName)
	case "chan string":
		return e.chanStringAction(writeS.(chan string), sheetName)
	case "map[string]string":
		return e.mapStringAction(writeS.(map[string]string))
	}
	return
}
func (e *Xls) Delete(_ any) (err error) {
	return
}
func (e *Xls) Rename(_ any) (err error) {
	return
}
